package group

import (
	"net/http"
	"reflect"
	"strconv"
	"strings"

	"gitee.com/haodreams/golib/easyroute/param"
	"gitee.com/haodreams/golib/session"
	"gitee.com/haodreams/libs/sliceutil"
	"github.com/gin-gonic/gin"
)

// 路由分组策略
type Group struct {
	funcGetSession func(*gin.Context) (session.Sessioner, error)
	root           string
	route          map[string]func(c *gin.Context)
	loginPage      string
}

// 设置登录页面
func (m *Group) SetLoginPage(loginPage string){
	m.loginPage = loginPage
}

func (m *Group) Size() int {
	return len(m.route)
}

// 设置认证函数
func (m *Group) WithSession(funcGetSession func(*gin.Context) (session.Sessioner, error)) {
	m.funcGetSession = funcGetSession
}

func (m *Group) CheckPath(c *gin.Context) bool {
	if m.funcGetSession != nil {
		sess, err := m.funcGetSession(c)
		if err != nil {
			if m.loginPage != "" {
				c.Redirect(302, m.loginPage)
				return false
			}
			m.Msg(c, err.Error(), http.StatusUnauthorized)
			return false
		}
		if !sess.CheckPath(c.Request.URL.Path) {
			m.Msg(c, "no permission", http.StatusForbidden)
			return false
		}
		// 可以将 sess 存储到 gin.Context 中
		c.Set("uid", sess)
	}
	return true
}

// 默认网关入口,
func (m *Group) DefaultGateway(c *gin.Context) {
	if !m.CheckPath(c) {
		return
	}

	path := strings.TrimPrefix(c.Request.URL.Path, m.root)
	pos := strings.IndexByte(path, '/')
	if pos > 0 {
		path = path[:pos]
	}
	f := m.route[path]
	if f == nil {
		m.Msg(c, "not found", http.StatusNotFound)
		return
	}
	f(c)
}

// 设置路由规则
func (m *Group) SetHandleFunc(name string, f func(c *gin.Context)) {
	if m.route == nil {
		m.route = make(map[string]func(c *gin.Context))
	}
	m.route[name] = f
}

// 获取跟目录
func (m *Group) Root() string {
	return m.root
}

// 设置路由根路径
func (m *Group) SetRoot(root string) {
	if !strings.HasSuffix(root, "/") {
		root += "/"
	}
	m.root = root
}

// 解析 url 参数
func (m *Group) Params(c *gin.Context) *param.Param {
	return param.NewParamByURL(c.Request.URL.RawQuery)
}

func (m *Group) IsPost(c *gin.Context) bool {
	return c.Request.Method == "POST"
}

func (m *Group) Msg(ctx *gin.Context, msg string, codes ...int) {
	code := http.StatusOK
	if len(codes) > 0 {
		code = codes[0]
	}
	mp := map[string]any{"code": code, "msg": msg}
	ctx.JSON(http.StatusOK, mp)
	//w.Header().Set("Content-Type", "application/json")
	//json.NewEncoder(w).Encode(m)
}

// msgcode [msg][code]
func (m *Group) Data(ctx *gin.Context, data any, msgcode ...any) {
	mp := map[string]any{}
	switch len(msgcode) {
	case 1:
		mp["msg"] = msgcode[0]
		mp["code"] = 200
	case 2:
		mp["msg"] = msgcode[0]
		mp["code"] = msgcode[1]
	default:
		mp["code"] = 200
		mp["msg"] = "OK"
	}
	mp["data"] = data
	ctx.JSON(http.StatusOK, mp)
}

func (m *Group) Map(ctx *gin.Context, dict map[string]any, msgcode ...any) {
	switch len(msgcode) {
	case 1:
		dict["msg"] = msgcode[0]
	case 2:
		dict["msg"] = msgcode[0]
		dict["code"] = msgcode[1]
	default:
		dict["code"] = 200
		dict["msg"] = "OK"
	}
	ctx.JSON(http.StatusOK, dict)
}

func (m *Group) SetCookie(ctx *gin.Context, name, value string, maxAge int) {
	ctx.SetCookie(name, value, maxAge, "/", "", false, true)
}

func (m *Group) CheckoutParams(c *gin.Context) *param.Param {
	params1 := c.Keys["params"]
	if params1 == nil {
		params, ok := params1.(*param.Param)
		if ok && params != nil {
			return params
		}
	}
	params := param.NewParamByURL(c.Request.URL.RawQuery)
	if c.Request.Method == "POST" {
		params.Form(c.Request)
	}
	c.Keys["params"] = params
	return params
}

func (m *Group) Rows(c *gin.Context, count int, rows any, msg string, codes ...int) {
	code := 0 //layui 必须是0
	if len(codes) > 0 {
		code = codes[0]
	}

	dict := map[string]any{}

	dict["count"] = count
	dict["data"] = rows

	m.Map(c, dict, msg, code)
}

// ToWhereSortPage 转换为WhereSort条件的页面
// isReg 字符串值时，是否使用正则
// callback 对选择的数据进行处理, 例如排序
func (m *Group) ToWhereSortPage(c *gin.Context, items any, callback func(any) any, isReg ...bool) {
	params := m.CheckoutParams(c)
	//先过滤 后排序
	mapParam := params.GetMap()

	newItems := sliceutil.WhereMap(mapParam, items, isReg...)

	sliceutil.OrderBy(newItems, mapParam["sortKey"], mapParam["sortOrder"])

	m.Page(c, newItems, callback)
}

// Page 显示指定页面的数据
// array 传入指定的数组
func (m *Group) Page(c *gin.Context, array any, callback func(any) any) {
	params := m.CheckoutParams(c)
	page, limit := m.GetPage(params)

	v := reflect.Indirect(reflect.ValueOf(array))
	if v.Kind() != reflect.Slice {
		m.Rows(c, 0, nil, "No data")
		return
	}

	begin := (page - 1) * limit
	end := page * limit
	n := v.Len()
	if end <= n {
		if callback == nil {
			m.Rows(c, n, v.Slice(begin, end).Interface(), "OK")
		} else {
			m.Rows(c, n, callback(v.Slice(begin, end).Interface()), "OK")
		}
	} else {
		end = n
		if begin >= n {
			m.Rows(c, n, nil, "OK")
		} else {
			if callback == nil {
				m.Rows(c, n, v.Slice(begin, n).Interface(), "OK")
			} else {
				m.Rows(c, n, callback(v.Slice(begin, n).Interface()), "OK")
			}
		}
	}
}

// Rows ...
// {"code":500,"count":0,"data":[], "msg":"` + emsg + `"}`))

// GetPage 获取分页位置
func (m *Group) GetPage(params *param.Param) (page, limit int) {
	return m.GetPageFromMap(params.GetMap())
}

// GetPageFromMap .
func (m *Group) GetPageFromMap(mp map[string]string) (page, limit int) {
	val := mp["page"]
	page, err := strconv.Atoi(val)
	if err != nil {
		page = 1
	}

	val = mp["limit"]
	limit, err = strconv.Atoi(val)
	if err != nil {
		limit = 50
	}

	if limit < 1 {
		limit = 1
	}

	if page < 1 {
		page = 1
	}
	return
}
